home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 2.iso
/
STUTTGART
/
TEMP
/
GNU
/
bison
/
MidRuleAct
< prev
next >
Wrap
Text File
|
1995-06-28
|
6KB
|
154 lines
Mid-Rule Actions
Previous: <Action Types=>ActionType> * Next: <Declarations=>Declaratio> * Up: <Semantics=>Semantics>
#Wrap on
{fH4}Actions in Mid-Rule{f}
Occasionally it is useful to put an action in the middle of a rule.
These actions are written just like usual end-of-rule actions, but they
are executed before the parser even recognizes the following components.
A mid-rule action may refer to the components preceding it using
{fCode}${fStrong}n{f}{f}, but it may not refer to subsequent components because
it is run before they are parsed.
The mid-rule action itself counts as one of the components of the rule.
This makes a difference when there is another action later in the same rule
(and usually there is another at the end): you have to count the actions
along with the symbols when working out which number {fStrong}n{f} to use in
{fCode}${fStrong}n{f}{f}.
The mid-rule action can also have a semantic value. The action can set
its value with an assignment to {fCode}$${f}, and actions later in the rule
can refer to the value using {fCode}${fStrong}n{f}{f}. Since there is no symbol
to name the action, there is no way to declare a data type for the value
in advance, so you must use the {fEmphasis}$<…>{f} construct to specify a
data type each time you refer to this value.
There is no way to set the value of the entire rule with a mid-rule
action, because assignments to {fCode}$${f} do not have that effect. The
only way to set the value for the entire rule is with an ordinary action
at the end of the rule.
Here is an example from a hypothetical compiler, handling a {fCode}let{f}
statement that looks like {fEmphasis}let ({fStrong}variable{f}) {fStrong}statement{f}{f} and
serves to create a variable named {fStrong}variable{f} temporarily for the
duration of {fStrong}statement{f}. To parse this construct, we must put
{fStrong}variable{f} into the symbol table while {fStrong}statement{f} is parsed, then
remove it afterward. Here is how it is done:
#Wrap off
#fCode
stmt: LET '(' var ')'
\{ $<context>$ = push\_context ();
declare\_variable ($3); \}
stmt \{ $$ = $6;
pop\_context ($<context>5); \}
#f
#Wrap on
As soon as {fEmphasis}let ({fStrong}variable{f}){f} has been recognized, the first
action is run. It saves a copy of the current semantic context (the
list of accessible variables) as its semantic value, using alternative
{fCode}context{f} in the data-type union. Then it calls
{fCode}declare\_variable{f} to add the new variable to that list. Once the
first action is finished, the embedded statement {fCode}stmt{f} can be
parsed. Note that the mid-rule action is component number 5, so the
{fEmphasis}stmt{f} is component number 6.
After the embedded statement is parsed, its semantic value becomes the
value of the entire {fCode}let{f}-statement. Then the semantic value from the
earlier action is used to restore the prior list of variables. This
removes the temporary {fCode}let{f}-variable from the list so that it won't
appear to exist while the rest of the program is parsed.
Taking action before a rule is completely recognized often leads to
conflicts since the parser must commit to a parse in order to execute the
action. For example, the following two rules, without mid-rule actions,
can coexist in a working parser because the parser can shift the open-brace
token and look at what follows before deciding whether there is a
declaration or not:
#Wrap off
#fCode
compound: '\{' declarations statements '\}'
| '\{' statements '\}'
;
#f
#Wrap on
But when we add a mid-rule action as follows, the rules become nonfunctional:
#Wrap off
#fCode
compound: \{ prepare\_for\_local\_variables (); \}
'\{' declarations statements '\}'
| '\{' statements '\}'
;
#f
#Wrap on
Now the parser is forced to decide whether to run the mid-rule action
when it has read no farther than the open-brace. In other words, it
must commit to using one rule or the other, without sufficient
information to do it correctly. (The open-brace token is what is called
the {fUnderline}look-ahead{f} token at this time, since the parser is still
deciding what to do about it. \*Note <Look-Ahead=>LookAhead>: Look-Ahead Tokens.)
You might think that you could correct the problem by putting identical
actions into the two rules, like this:
#Wrap off
#fCode
compound: \{ prepare\_for\_local\_variables (); \}
'\{' declarations statements '\}'
| \{ prepare\_for\_local\_variables (); \}
'\{' statements '\}'
;
#f
#Wrap on
But this does not help, because Bison does not realize that the two actions
are identical. (Bison never tries to understand the C code in an action.)
If the grammar is such that a declaration can be distinguished from a
statement by the first token (which is true in C), then one solution which
does work is to put the action after the open-brace, like this:
#Wrap off
#fCode
compound: '\{' \{ prepare\_for\_local\_variables (); \}
declarations statements '\}'
| '\{' statements '\}'
;
#f
#Wrap on
Now the first token of the following declaration or statement,
which would in any case tell Bison which rule to use, can still do so.
Another solution is to bury the action inside a nonterminal symbol which
serves as a subroutine:
#Wrap off
#fCode
subroutine: \/\* empty \*\/
\{ prepare\_for\_local\_variables (); \}
;
compound: subroutine
'\{' declarations statements '\}'
| subroutine
'\{' statements '\}'
;
#f
#Wrap on
Now Bison can execute the action in the rule for {fCode}subroutine{f} without
deciding which rule for {fCode}compound{f} it will eventually use. Note that
the action is now at the end of its rule. Any mid-rule action can be
converted to an end-of-rule action in this way, and this is what Bison
actually does to implement mid-rule actions.